Powershell scripts/Machines Not Protected by Defender for SQL Extension/list-notProtectedMachinesDefenderforSQL.ps1 (205 lines of code) (raw):

#Requires -Modules Az, Az.Resources, Az.Accounts, Az.Compute, Az.ConnectedMachine, Az.SqlVirtualMachine <# .SYNOPSIS Finds SQL VMs and Arc machines not protected by Defender for SQL servers on machines. .DESCRIPTION This script identifies SQL VMs and Arc machines that lack Defender for SQL servers on machines protection. .PARAMETER SubscriptionId [Required] The Azure subscription ID for enabling Defender for SQL servers on machines. #> param( [Parameter(Mandatory=$true)] [string]$SubscriptionId ) try { # Log in to Azure if not already logged in Connect-AzAccount -Subscription $SubscriptionId -ErrorAction Stop # Retrieve all Arc machine resources and SQL VMs from the selected subscription Write-Host "Retrieving all Arc machine resources and VMs from subscription $($SubscriptionId)" $allResources = Get-AzResource | Where-Object { $_.ResourceType -match 'Microsoft.HybridCompute/machines|Microsoft.Compute/virtualMachines' } } catch { Write-Error "Unable to retrieve Arc resources and SQL VMs from subscriptio $($SubscriptionId), error: $_" Write-Error "Stopping the script." exit 1 } # Filter Arc machines and VMs $ArcResources = $allResources | Where-Object { $_.ResourceType -eq 'Microsoft.HybridCompute/machines' } $VMResources = $allResources | Where-Object { $_.ResourceType -eq 'Microsoft.Compute/virtualMachines' } # Initialize lists to store not protected machines and their errors $nonCompliantVMs = @() $nonCompliantVMsError = @() Write-Host "-----------------------" # Iterate over all SQL VMs in the subscription to identify not protected VMs Write-Host "Checking all SQL VMs in the subscription for compliance" foreach ($vm in $VMResources) { try { # Obtain the instance view of the VM $vmInstanceView = Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status -ErrorAction Stop } catch { Write-Warning "Unable to obtain status for machine $($vm.Name). Exception: $_" continue } try { # Obtain the instance view of the SQL VM $sqlVMInstanceView = Get-AzSqlVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -ErrorAction Stop } catch { Write-Host "VM $($vm.Name) is missing SQL extension, skipping.." continue } # Exclude deallocated machines $powerState = ($vmInstanceView.Statuses | Where-Object { $_.Code -like 'PowerState/*' }).DisplayStatus if (-not $powerState -or $powerState.ToLower() -ne 'vm deallocated') { # Exclude Linux machines if ($vmInstanceView.OsName.ToLower() -like "*windows*") { # Verify the presence of extensions if ($vmInstanceView.Extensions) { # Search for the Defender for SQL extension $defenderForSQLExtension = $vmInstanceView.Extensions | Where-Object { $_.Type -ne $null -and $_.Type.ToLower() -eq 'microsoft.azure.azuredefenderforsql.advancedthreatprotection.windows' } if ($defenderForSQLExtension) { $addToNonCompliant = $false $errorMsg = "" # Inspect the extension statuses if ($defenderForSQLExtension.Statuses) { foreach ($status in $defenderForSQLExtension.Statuses) { $message = $status.Message.ToLower() $displayStatus = $status.DisplayStatus.ToLower() # Validate the status message if ($displayStatus -ne $null -and $displayStatus -like "*provisioning succeeded*") { if ($message -ne $null -and $message -notlike "*extension is running*") { Write-Host "VM $($vm.Name) is not protected. Defender for SQL extension status contains errors." $errorMsg = $message $addToNonCompliant = $true } } else { Write-Host "VM $($vm.Name) is not protected. Defender for SQL extension provisioning failed." $errorMsg = "Defender for SQL extension provisioning failed, $message" $addToNonCompliant = $true } } } else { Write-Host "VM $($vm.Name) is not protected. Missing Defender for SQL extension status." $errorMsg = "Missing Defender for SQL extension status." $addToNonCompliant = $true } if ($addToNonCompliant) { $nonCompliantVMs += $vm.Name $nonCompliantVMsError += $errorMsg } } else { Write-Host "VM $($vm.Name) is not protected. Defender for SQL extension is missing." $errorMsg = "Defender for SQL extension is missing." $nonCompliantVMs += $vm.Name $nonCompliantVMsError += $errorMsg } } else { Write-Host "VM $($vm.Name) is missing extensions, skipping.." } } else { Write-Host "VM $($vm.Name) OS type is Linux, skipping.." } } else { Write-Host "VM $($vm.Name) is deallocated. Missing extensions information. skipping.." } } # Initialize a list to store Arcs that don't meet the criteria $nonCompliantArcs = @() $nonCompliantArcsError = @() Write-Host "-----------------------" # Iterate over all Arc machines in the subscription to identify not protected Arc machines Write-Host "Checking all Arc machines in the subscription for compliance machines" foreach ($arc in $ArcResources) { try { # Obtain the instance view of the Arc machine $arcInstanceView = Get-AzConnectedMachine -ResourceGroupName $arc.ResourceGroupName -MachineName $arc.Name -ErrorAction Stop $arcInstanceExtensionView = Get-AzConnectedMachineExtension -ResourceGroupName $arc.ResourceGroupName -MachineName $arc.Name -Expand StatusMessage -ErrorAction Stop } catch { Write-Warning "Unable to obtain status for machine $($arc.Name). Exception: $_" continue } # Exclude not connected machines if ($arcInstanceView.Status -ne $null -and $arcInstanceView.Status -like 'Connected') { # Exclude Linux machines if ($arcInstanceView.OSType.ToLower() -like "windows") { $addToNonCompliant = $false $errorMsg = "" # Search for the Defender for SQL extension $defenderForSQLExtension = $arcInstanceExtensionView | Where-Object { $_.Publisher -ne $null -and $_.Publisher.ToLower() -eq 'microsoft.azure.azuredefenderforsql' } # Search for the SQL IaaS extension $sqlExtension = $arcInstanceExtensionView | Where-Object { $_.Publisher -ne $null -and $_.Publisher.ToLower() -eq 'microsoft.azuredata' -and $_.Name -ne $null -and $_.Name.ToLower() -eq 'windowsagent.sqlserver' } if ($sqlExtension) { if ($defenderForSQLExtension -ne $null) { # Inspect the extension provisioning state $displayStatus = $defenderForSQLExtension.ProvisioningState.ToLower() if ($displayStatus -ne $null -and $displayStatus -like "*Succeeded*") { # Inspect the extension statuses if ($defenderForSQLExtension.StatusMessage -ne $null) { $message = $defenderForSQLExtension.StatusMessage.ToLower() # Validate the status message if ($message -ne $null -and $message -notlike "*extension is running*") { Write-Host "Arc machine $($arc.Name) is not protected. Defender for SQL extension status contains errors." $errorMsg = $message $addToNonCompliant = $true } } else { Write-Host "Arc machine $($arc.Name) is not protected. Missing Defender for SQL extension status." $errorMsg = "Missing Defender for SQL extension status." $addToNonCompliant = $true } } else { Write-Host "Arc machine $($arc.Name) is not protected. Defender for SQL extension provisioning failed." $errorMsg = "Defender for SQL extension provisioning failed, $($defenderForSQLExtension.StatusMessage)" $addToNonCompliant = $true } if ($addToNonCompliant) { $nonCompliantArcs += $arc.Name $nonCompliantArcsError += $errorMsg } } else { Write-Host "Arc machine $($arc.Name) is not protected. Defender for SQL extension is missing." $errorMsg = "Defender for SQL extension is missing." $nonCompliantArcs += $arc.Name $nonCompliantArcsError += $errorMsg } } else { Write-Host "Arc machine $($arc.Name) is missing SQL extension, skipping.." } } else { Write-Host "Arc machine $($arc.Name) OS type is Linux, skipping.." } } else { Write-Host "Arc machine $($arc.Name) is not connected. Missing extensions information. skipping.." } } # Output the list of not protected VMs Write-Host "-----------------------" if ($nonCompliantVMs.Count -gt 0) { Write-Host "The following VMs are not protected:" for ($i = 0; $i -lt $nonCompliantVMs.Count; $i++) { Write-Host "VM: $($nonCompliantVMs[$i]), error: $($nonCompliantVMsError[$i])" } } else { Write-Host "After checking all connected VMs, no not protected VMs were found. To test deallocated VMs, please turn them on and re-run the script." } # Output the list of not protected Arc machines Write-Host "-----------------------" if ($nonCompliantArcs.Count -gt 0) { Write-Host "The following Arc machines are not protected:" for ($i = 0; $i -lt $nonCompliantArcs.Count; $i++) { Write-Host "Arc machine: $($nonCompliantArcs[$i]), error: $($nonCompliantArcsError[$i])" } } else { Write-Host "After checking all connected Arc machines, no not protected Arc machines were found. To test disconnected Arc machines, please turn them on and re-run the script." }